/*******************************************************************/
/* @ All Rights Reserved Copyright (C) 2015 broada,Co.,Ltd.        */
/*                                                                 */
/*  NAME      = testtools_compare.c                                */
/*  NOTE      = 比较测试数据和预想结果是否一致                     */
/*  DATE      = 2015/11/03 by guojq                                */
/*******************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <json-c/json.h>
#include <errno.h>

#include "frcomm.h"
#include "frmem.h"
#include "frstr.h"
#include "frsutl.h"
#include "frlist.h"
#include "frxml_parse.h"
#include "test_comm.h"

#define BUF_SIZE   1024

static Frlist_header g_st_old_head;
static Frlist_header g_st_new_head;
static char *g_p_test_file = NULL;
static char *g_p_old_file = NULL;
static char *g_p_config_file = NULL;

static int __json_type_count(json_object *p_obj, int *pp_i_count);
static int __json_object_count(json_object *p_obj, int *pp_i_count);
static int __json_del_obj_key(json_object *p_obj, char *p_key);
static int __json_type_del_key(json_object *p_obj, char *p_key);

Frtesttools_params st_testtools_params;

static void __usage(char *p_pgname)
{
    printf("Usage: %s json_file1 json_file2 config_file\n", p_pgname);
}

static void __file_node_free(Frlist_node* node)
{
    Frstr *p_str = NULL;

    if (node == NULL || node->data == NULL)
        return;
    p_str = (Frstr *)node->data;
    frstr_free(&p_str);
}

static int __testtools_read_file(char *p_file, Frlist_header *p_list_head)
{
    FILE *fp = NULL;
    char arr_buf[BUF_SIZE];
    char *p_tmp = NULL;
    int i_line_head = 0;
    int i_buf_skip = 0;
    Frstr *p_st_line = NULL;
    Frlist_node *p_st_new = NULL;
    
    fp = fopen(p_file, "r");
    if (fp == NULL)
    {
        printf("ERR,文件[%s]打开失败, %s\n", p_file, strerror(errno));
        return FR_FAIL;
    }
    
    while (fgets(arr_buf, sizeof(arr_buf) - 1, fp) != NULL)
    {
        //接收一行完整的数据
        if (arr_buf[strlen(arr_buf) - 1] == '\n')
        {
            if (i_line_head == 0)
            {
                p_tmp = strchr(arr_buf, ':');
                if (p_tmp == NULL)
                {
                    //收到1个比较小的数，不到长度，先保存起来
                    if (strlen(arr_buf) > 5)
                        goto err;
                    continue;
                }
                //长度不存到链表中
                i_buf_skip = p_tmp - arr_buf + 1;
                
                p_st_line = frstr_new(1024);
                if (p_st_line == NULL)
                    goto err;
            }
            i_line_head = 0;
            arr_buf[strlen(arr_buf) - 1] = '\0';
            
            if (FRSTR_APPEND_STR(p_st_line, arr_buf + i_buf_skip) != 0)
                goto err;
            
            p_st_new = frlist_node_create(p_st_line);
            if (p_st_new == NULL)
                goto err;
            
            frlist_add_tail(p_list_head, p_st_new);
            
            i_buf_skip = 0;
            p_st_line = NULL;
            continue;
        }
        if (i_line_head == 0)
        {
            p_tmp = strchr(arr_buf, ':');
            if (p_tmp == NULL)
            {
                //收到1个比较小的数，不到长度，先保存起来
                if (strlen(arr_buf) > 5)
                    goto err;
                continue;
            }
            //长度不存到链表中
            i_buf_skip = p_tmp - arr_buf + 1;
            
            p_st_line = frstr_new(1024);
            if (p_st_line == NULL)
                goto err;
        }
        i_line_head = 1;
        if (FRSTR_APPEND_STR(p_st_line, arr_buf + i_buf_skip) != 0)
            goto err;
        
        i_buf_skip = 0;
    }
    if (p_st_line)
    {
        p_st_new = frlist_node_create(p_st_line);
        if (p_st_new == NULL)
            goto err;
        
        frlist_add_tail(p_list_head, p_st_new);
    }
    fclose(fp);
    
    return FR_SUCC;
err:
    if (p_st_line)
        frstr_free(&p_st_line);
    fclose(fp);
    return FR_FAIL;
}

static int __json_object_count(json_object *p_obj, int *pp_i_count)
{
    if (!p_obj)
        return FR_FAIL;
        
    json_object_object_foreach(p_obj,key,val)
    {
        __json_type_count(val, pp_i_count);
    }
    return FR_SUCC;
}

static int __json_array_count(json_object *p_obj, int *pp_i_count)
{
    int i = 0;
    int i_length = 0;
    
    if(!p_obj)
        return FR_FAIL;
    
    i_length = json_object_array_length(p_obj);
    
    for(i=0;i<i_length;i++)
    {
        json_object *p_val=json_object_array_get_idx(p_obj,i);
        __json_type_count(p_val, pp_i_count);
    }
    return FR_SUCC;
}

static int __json_type_count(json_object *p_obj, int *pp_i_count)
{
    if (!p_obj)
        return FR_FAIL;
    
    json_type type = json_object_get_type(p_obj);
    
    if(type == json_type_boolean) {
        (*pp_i_count)++;
    } else if(type == json_type_double) {
        (*pp_i_count)++;
    } else if(type == json_type_int) {
        (*pp_i_count)++;
    } else if(type == json_type_string) {
        (*pp_i_count)++;
    } else if(type == json_type_object) {
        __json_object_count(p_obj, pp_i_count);
    } else if(type == json_type_array) {
        __json_array_count(p_obj, pp_i_count);
    } else {
        printf("ERR,json解析失败\n");
    }
    return FR_SUCC;
}

static int __json_count(char *p_buff, int *pp_i_count)
{
    json_object *p_obj = NULL;
    
    if (p_buff == NULL)
        return FR_FAIL;
    
    p_obj = json_tokener_parse(p_buff);
    
    if (__json_type_count(p_obj, pp_i_count) != FR_SUCC)
    {
        json_object_put(p_obj);
        return FR_FAIL;
    }
    json_object_put(p_obj);
     
    return FR_SUCC;
}

//遍历json数组做删除
static int __json_del_array_key(json_object *p_obj, char *p_key)
{
    int i = 0;
    int i_length = 0;
    
    if(!p_obj)
        return FR_FAIL;
    
    i_length = json_object_array_length(p_obj);
    
    for(i=0;i<i_length;i++)
    {
        json_object *p_obj_val = json_object_array_get_idx(p_obj,i);
        if (__json_type_del_key(p_obj_val, p_key) == FR_SUCC)
            return FR_SUCC;
    }
    return FR_FAIL;
}

//根据json的类型来删除
static int __json_type_del_key(json_object *p_obj, char *p_key)
{
    json_type type;
    
    if (!p_obj)
        return FR_FAIL;
    
    type = json_object_get_type(p_obj);
    
    if (type == json_type_object) {
        return __json_del_obj_key(p_obj, p_key);
    } else if(type == json_type_array) {
        return __json_del_array_key(p_obj, p_key);
    }
    return FR_FAIL;
}

//逐个json 来删除
static int __json_del_obj_key(json_object *p_obj, char *p_key)
{
    if (!p_obj)
        return FR_FAIL;
    json_object_object_foreach(p_obj,key,val)
    {
        if (strcasecmp(p_key, key) == 0)
        {
            json_object_object_del(p_obj, p_key);
            return FR_SUCC;
        }
        if (__json_type_del_key(val, p_key) == FR_SUCC)
            return FR_SUCC;
    }
    return FR_FAIL;
}

//FR_FAIL:不一致  FR_SUCC:一致
static int __json_analyse_cmp(char *p_buff_old, char *p_buff_new)
{
    json_object *p_obj_old = NULL;
    json_object *p_obj_new = NULL;
    Frlist_node *p_node = NULL;
    char *p_old = NULL, *p_new = NULL;
    
    p_obj_old = json_tokener_parse(p_buff_old);
    p_obj_new = json_tokener_parse(p_buff_new);
    
    p_node = FRLIST_FIRST_NODE(st_testtools_params.p_list_ignore_string);
    while (p_node && p_node->data)
    {
        Frvar_field *p_var_field = (Frvar_field *)p_node->data;
        if (p_var_field->field_value.p_value)
        {
            __json_del_obj_key(p_obj_old, p_var_field->field_value.p_value);
            __json_del_obj_key(p_obj_new, p_var_field->field_value.p_value);
        }
        p_node = FRLIST_NEXT_NODE(p_node);
    }
    
    p_old = (char *)(json_object_to_json_string(p_obj_old));
    p_new = (char *)(json_object_to_json_string(p_obj_new));
    
    if (strcmp(p_old, p_new))
    {
        json_object_put(p_obj_old);
        json_object_put(p_obj_new);
        return FR_FAIL;
    }
    
    json_object_put(p_obj_old);
    json_object_put(p_obj_new);
    
    return FR_SUCC;
}

static int __content_compare(Frlist_header *p_st_old_head, Frlist_header *p_st_new_head)
{
    Frlist_node *p_node = NULL;
    Frlist_node *p_node_new = NULL;
    Frstr *p_str_json_old = NULL;
    Frstr *p_str_json_new = NULL;
    int i_count = 0;
    int i_old_json_count = 0, i_new_json_count = 0;
    
    p_node = FRLIST_FIRST_NODE(p_st_old_head);
    while (p_node && p_node->data)
    {
        p_str_json_old = (Frstr *)p_node->data;
        //取得old的一个report的json个数
        if (__json_count(p_str_json_old->p_str, &i_old_json_count) != FR_SUCC)
        {
            printf("ERR,解析json数据失败, file=[%s] line=[%d]\n", g_p_old_file, i_count+1);
            return FR_FAIL;
        }
        //取得对应的new的一个report的json个数
        p_node_new = frlist_node_idx(p_st_new_head, i_count);
        if (p_node_new == NULL || p_node_new->data == NULL)
        {
            printf("ERR,解析json数据失败, file=[%s] line=[%d]\n", g_p_test_file, i_count+1);
            return FR_FAIL;
        }
        p_str_json_new = (Frstr *)p_node_new->data;
        if (__json_count(p_str_json_new->p_str, &i_new_json_count) != FR_SUCC)
        {
            printf("ERR,解析json数据失败, file=[%s] line=[%d]\n", g_p_old_file, i_count+1);
            return FR_FAIL;
        }
        //比较对应的json数据字符串个数
        if (i_old_json_count != i_new_json_count)
        {
            printf("ERR,文件的第%d个report信息的字段个数不匹配\n", i_count+1);
            return FR_FAIL;
        }
        
        //比较对应的json数据的内容
        if (__json_analyse_cmp(p_str_json_old->p_str, p_str_json_new->p_str) != FR_SUCC)
        {
            printf("ERR,不匹配, file=[%s] line=[%d]\n", g_p_old_file, i_count+1);
            return FR_FAIL;
        }
        
        p_node = FRLIST_NEXT_NODE(p_node);
        i_count++;
    }
    
    return FR_SUCC;
}

static int __testtools_compare_file(char *p_old_file, char *p_new_file)
{
    frlist_header_init(&g_st_old_head);
    frlist_header_init(&g_st_new_head);
    
    //解析对比的report文件存放到链表中
    if (__testtools_read_file(p_old_file, &g_st_old_head) != FR_SUCC)
    {
        printf("ERR,解析文件[%s] error\n", p_old_file);
        return FR_FAIL;
    }
    
    //解析测试结果的report文件存放到链表中
    if (__testtools_read_file(p_new_file, &g_st_new_head) != FR_SUCC)
    {
        printf("ERR,解析文件[%s] error\n", p_new_file);
        frlist_remove_all(&g_st_old_head, __file_node_free);
        return FR_FAIL;
    }
    
    //比较2个report信息的个数
    if (frlist_size(&g_st_old_head) != frlist_size(&g_st_new_head))
    {
        printf("ERR,文件[%s]和文件[%s]所拥有的json个数不一致\n", p_old_file, p_new_file);
        goto err;
    }
    
    //比较json数据的内容是否一致
    if (__content_compare(&g_st_old_head, &g_st_new_head) != FR_SUCC)
        goto err;
    
    frlist_remove_all(&g_st_old_head, __file_node_free);
    frlist_remove_all(&g_st_new_head, __file_node_free);
    
    return FR_SUCC;
err:
    frlist_remove_all(&g_st_old_head, __file_node_free);
    frlist_remove_all(&g_st_new_head, __file_node_free);
    return FR_FAIL;
}

static int __config_get(char *p_config_file)
{
    int i_ret_fun=0;
    char *p_key=NULL;
    Frlist_header *p_st_head = NULL;
    
    p_key = "ignore_string";
    i_ret_fun = frxml_get_string_list(p_config_file, "/testtools/ignore_strings", p_key, &p_st_head);
    if(i_ret_fun == FR_SUCC)
        st_testtools_params.p_list_ignore_string = p_st_head;
    else
    {
        printf("ERR,读取配置文件[%s]中ignore_strings失败\n", p_config_file);
        return FR_FAIL;
    }
    
    return FR_SUCC;
}
int main (int argc, char *argv[])
{
    int i_ret_fun = 0;
    
    if (argc < 4)
    {
        __usage(argv[0]);
        return 1;
    }
    g_p_test_file = argv[1];
    g_p_old_file = argv[2];
    g_p_config_file = argv[3];
    
    i_ret_fun = fr_slab_init(20971520, "testtools_compare");//20M
    if(i_ret_fun == FR_FAIL)
    {
        printf("ERR,fr_slab_init内存分配失败\n");
        return 1;
    }
    memset(&st_testtools_params, 0x00, sizeof(Frtesttools_params));
    
    //读取配置文件的内容
    if (__config_get(g_p_config_file) != FR_SUCC)
        return 1;
    
    if (__testtools_compare_file(g_p_old_file, g_p_test_file) != FR_SUCC)
        goto err;
    
    printf("OK,文件[%s]和文件[%s]信息一致\n", g_p_test_file, g_p_old_file);
    
    frxml_free_list(&st_testtools_params.p_list_ignore_string);
    
    fr_slab_destroy();
    return 0;
err:
    if (st_testtools_params.p_list_ignore_string)
        frxml_free_list(&st_testtools_params.p_list_ignore_string);
    fr_slab_destroy();
    return 1;
}

